#include "jsimplifiedchinesecandidatepanel.h"
#include "ui_jsimplifiedchinesecandidatepanel.h"
#include "jgooglepinyin.h"

#include <QTECore/QTEGlobal>
#include <QGuiApplication>
#include <QStringList>
#include <QPushButton>
#include <QScreen>

namespace QTE
{

CandidatePanel_register(JSimplifiedChineseCandidatePanel, LGG_SimplifiedChinese);

class Q_DECL_HIDDEN JSimplifiedChineseCandidatePanelPrivate
{
public:
	explicit JSimplifiedChineseCandidatePanelPrivate(JSimplifiedChineseCandidatePanel *q_ptr);
	~JSimplifiedChineseCandidatePanelPrivate();

public:
	void pinyinLabelFlush(const QString &pinyin);
	void chineseListFlush();

public:
	void update(const QString &pinyin);
	bool defaultInput(bool finished = false);

public:
	int pinYinCounterAdd(int n);
	int pinYinCounterSub();

public:
	void clear(bool clearChinese = true);

public:
	JSimplifiedChineseCandidatePanel *q_ptr;
	Ui::JSimplifiedChineseCandidatePanel *ui;

	JGooglePinyin &m_googlePinyin;

	QStringList m_chineseCache;
	QString m_pinYinCache;

	std::list<int> m_pinYinCounterList;
	int m_pinYinCounter = 0;
	int m_index = 0;
};

JSimplifiedChineseCandidatePanelPrivate::
JSimplifiedChineseCandidatePanelPrivate(JSimplifiedChineseCandidatePanel *q_ptr) :
	q_ptr(q_ptr),
	ui(new Ui::JSimplifiedChineseCandidatePanel),
	m_googlePinyin(JGooglePinyin::instance())
{
	ui->setupUi(q_ptr);

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
	auto color = q_ptr->palette().color(QPalette::Background);
#else
	auto color = q_ptr->palette().color(QPalette::Active, QPalette::Window);
#endif
	color.setRgb(255 - color.red(), 255 - color.green(), 255 - color.blue());

	// 静态item，动态内容
	for(int i=0; i<LIST_MAX_NUM; i++)
	{
		auto item = new QListWidgetItem(ui->listWidget);
		auto button = new QPushButton();

		button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
		button->setFocusPolicy(Qt::NoFocus);

		button->setStyleSheet(QString("background: rgba(0,0,0,0);"
									  "border-left-color: rgb(%1,%2,%3);"
									  "border-right-color: rgb(%1,%2,%3);")
							  .arg(color.red()).arg(color.green()).arg(color.blue()));

		ui->listWidget->setItemWidget(item, button);

		QObject::connect(button, &QPushButton::clicked, q_ptr, [this, i, button]()
		{
			if( m_pinYinCache.isEmpty() )
				return this->q_ptr->JAbstractCandidatePanel::input(button->text().remove(0,2));

			m_googlePinyin.choose(i);

			if( m_googlePinyin.chineseList.size() == 1 )
			{
				m_chineseCache << m_googlePinyin.chineseList[0];
				pinYinCounterAdd(m_googlePinyin.pinYinCache.size());

				if( m_pinYinCache.size() > m_pinYinCounter )
				{
					QString tmp = m_pinYinCache.mid(m_pinYinCounter);
					update(tmp);
				}
				else
				{
					this->q_ptr->JAbstractCandidatePanel::input(m_chineseCache.join(""));
					clear(false);
				}
			}
			else
				chineseListFlush();
		});
		item->setHidden(true);
	}

	ui->listWidget->setAutoScrollBar(false);
	ui->listWidget->hide();
}

JSimplifiedChineseCandidatePanelPrivate::~JSimplifiedChineseCandidatePanelPrivate()
{
	delete ui;
}

static inline bool isLetter(const QChar &c)
{
	return (c >= 'A' and c <= 'Z') or (c >= 'a' and c <= 'z');
}

void JSimplifiedChineseCandidatePanelPrivate::pinyinLabelFlush(const QString &pinyin)
{
	if( pinyin.isEmpty() )
		return ui->pinyinLabel->setText(pinyin);

	int index = m_index - m_pinYinCounter;
	if( index == 0 )
		return ui->pinyinLabel->setText(m_chineseCache.join("") + " |" + pinyin);

	else if( index < 0 )
	{
		m_index = m_pinYinCounter;
		return ui->pinyinLabel->setText(m_chineseCache.join("") + " |" + pinyin);
	}

	int it = 0;
	for(int i=0; it<pinyin.size(); it++)
	{
		if( not isLetter(pinyin[it]) )
			continue;

		else if( ++i == index )
			break;
	}

	QString tmp = pinyin;
	tmp.insert(it + 1, "|");

	if( not m_chineseCache.isEmpty() )
		tmp = m_chineseCache.join("") + " " + tmp;

	ui->pinyinLabel->setText(tmp);
}

void JSimplifiedChineseCandidatePanelPrivate::chineseListFlush()
{
	int i = 0;
	for(; i<m_googlePinyin.chineseList.size(); i++)
	{
		auto item = ui->listWidget->item(i);
		if( item == nullptr )
			continue;

		auto button = J_OCT(QAbstractButton*, ui->listWidget->itemWidget(item));
		if( button == nullptr )
			continue;

		auto text = QString("%1.%2").arg(i+1).arg(m_googlePinyin.chineseList[i]);

		QFontMetrics metrics(button->font());
		auto size = metrics.boundingRect(text).size();

		size.rwidth() += metrics.boundingRect("华").size().width() >> 1;
		size.rheight() = ui->listWidget->height();
		item->setSizeHint(size);

		button->setFixedSize(size);
		button->setText(text);
		item->setHidden(false);
	}

	for(; i<LIST_MAX_NUM; i++)
	{
		auto item = ui->listWidget->item(i);
		if( item != nullptr )
			item->setHidden(true);
	}
	ui->listWidget->setVisible(not m_googlePinyin.chineseList.isEmpty());
}

void JSimplifiedChineseCandidatePanelPrivate::update(const QString &pinyin)
{
	pinyinLabelFlush(m_googlePinyin.searchCN(pinyin));
	chineseListFlush();
}

bool JSimplifiedChineseCandidatePanelPrivate::defaultInput(bool finished)
{
	bool res = false;
	QString text = ui->pinyinLabel->text();

	auto lambda = [this, &res, &text]()
	{
		auto item = ui->listWidget->item(0);

		if( item == nullptr or item->isHidden() )
		{
			if( not text.isEmpty() )
			{
				this->q_ptr->JAbstractCandidatePanel::input(text.remove("|"));
				clear();
				res = true;
			}
			return ;
		}

		auto button = J_OCT(QAbstractButton*, ui->listWidget->itemWidget(item));
		if( button != nullptr and not button->text().isEmpty() )
		{
			button->click();
			res = true;
		}
	};

	if( finished )
	{
		if( text.isEmpty() )
		{
			clear();
			return false;
		}

		else if( isPureAscii(text) )
			lambda();
		else
		{
			this->q_ptr->JAbstractCandidatePanel::input(text.remove("|"));
			clear();
			res = true;
		}
	}
	else
		lambda();

	return res;
}

int JSimplifiedChineseCandidatePanelPrivate::pinYinCounterAdd(int n)
{
	m_pinYinCounterList.push_back(n);
	m_pinYinCounter += n;
	return m_pinYinCounter;
}

int JSimplifiedChineseCandidatePanelPrivate::pinYinCounterSub()
{
	Q_ASSERT(not m_pinYinCounterList.empty());
	int n = m_pinYinCounterList.back();

	m_pinYinCounterList.pop_back();
	m_pinYinCounter -= n;

	return m_pinYinCounter;
}

void JSimplifiedChineseCandidatePanelPrivate::clear(bool clearChinese)
{
	m_pinYinCounter = 0;
	m_pinYinCache = ""; //'clear' has a bug on some platforms.

	m_chineseCache.clear();
	m_index = 0;

	pinyinLabelFlush(m_googlePinyin.searchCN(""));
	if( clearChinese )
		chineseListFlush();
}

/*------------------------------------------------------------------------------------------------*/

JSimplifiedChineseCandidatePanel::JSimplifiedChineseCandidatePanel(QWidget *parent) :
	JAbstractCandidatePanel(parent),
	d_ptr(new JSimplifiedChineseCandidatePanelPrivate(this))
{
	qreal c = -13.0 / 132000.0 *
			  J_SCT(qreal, qApp->primaryScreen()->size().height()) +
			  651.0 / 3300.0;

	setMaximumHeight(J_SCT(int, c * J_SCT(qreal, qApp->primaryScreen()->size().height())));
}

JSimplifiedChineseCandidatePanel::~JSimplifiedChineseCandidatePanel()
{
	delete d_ptr;
}

void JSimplifiedChineseCandidatePanel::predicts(const QString &text)
{
	if( d_ptr->m_pinYinCache.isEmpty() )
	{
		d_ptr->m_googlePinyin.predicts(text);
		d_ptr->chineseListFlush();
	}
}

static bool check(QString &str)
{
	for(auto &c : str)
	{
		if( c == QString("ü") )
			c = 'v';
		else if( c == QString("ɑ") )
			c = 'a';

		else if( c == '\'' )
			continue;

		else if( not isLetter(c) )
			return false;
	}
	return true;
}

void JSimplifiedChineseCandidatePanel::input(const QString &text)
{
	auto tmp = text;
	if( check(tmp) )
	{
		int residue = 32 - d_ptr->m_pinYinCache.size();
		if( tmp.size() > residue )
		{
			tmp.chop(tmp.size() - residue);
			if( tmp.isEmpty() )
				return ;
		}

		d_ptr->m_pinYinCache.insert(d_ptr->m_index, tmp);
		QString pinyin = d_ptr->m_pinYinCache.mid(d_ptr->m_pinYinCounter);

		d_ptr->m_index += tmp.size();
		d_ptr->update(pinyin);
	}

	else if( text == " " )
	{
		if( d_ptr->defaultInput() == false )
			JAbstractCandidatePanel::input(text);
	}

	else if( d_ptr->m_pinYinCache.isEmpty() )
		JAbstractCandidatePanel::input(text);
}

void JSimplifiedChineseCandidatePanel::input(Qt::Key key)
{
	if( key == Qt::Key_Backspace )
	{
		if( d_ptr->m_pinYinCache.isEmpty() )
			return JAbstractCandidatePanel::input(key);

		if( d_ptr->m_googlePinyin.cancelChoose() >= 0 )
			d_ptr->chineseListFlush();

		else if( not d_ptr->m_chineseCache.isEmpty() )
		{
			d_ptr->m_chineseCache.pop_back();
			d_ptr->update(d_ptr->m_pinYinCache.mid(d_ptr->pinYinCounterSub()));
		}

		else
		{
			d_ptr->m_pinYinCache.remove(--d_ptr->m_index, 1);
			d_ptr->update(d_ptr->m_pinYinCache);
		}
	}

	else if( key == Qt::Key_Left )
	{
		if( d_ptr->m_index > d_ptr->m_pinYinCounter )
			--d_ptr->m_index;
		d_ptr->pinyinLabelFlush(d_ptr->m_googlePinyin.pinYinText);
	}

	else if( key == Qt::Key_Right )
	{
		if( d_ptr->m_index < d_ptr->m_pinYinCache.size() )
			++d_ptr->m_index;
		d_ptr->pinyinLabelFlush(d_ptr->m_googlePinyin.pinYinText);
	}

	else if( key == Qt::Key_Space )
	{
		if( d_ptr->defaultInput() == false )
			JAbstractCandidatePanel::input(key);
	}

	else if( d_ptr->m_pinYinCache.isEmpty() )
		JAbstractCandidatePanel::input(key);
}

void JSimplifiedChineseCandidatePanel::defaultInput()
{
	d_ptr->defaultInput(true);
}

void JSimplifiedChineseCandidatePanel::hideEvent(QHideEvent *event)
{
	JAbstractCandidatePanel::hideEvent(event);
	d_ptr->clear();
}

} //namespace QTE
